#include "Socket.hpp"
#include "InetAddr.hpp"
#include <cstring>
#include <poll.h>
#include <memory>
using namespace socket_ns;

class PollServer
{
    void reserve(int n)
    {
        if (n > _capacity)
        {
            struct pollfd *tmp = new struct pollfd[n];
            if (_pfd)
            {
                memcpy(tmp, _pfd, sizeof(struct pollfd *) * _size);
                delete _pfd;
            }
            _pfd = tmp;
            _capacity = n;
        }
    }
    void remove(int pos)
    {
        int end = pos + 1;
        while (end < _size)
        {
            _pfd[end - 1] = _pfd[end];
            end++;
        }
        _size--;
    }

public:
    PollServer(uint16_t port)
        : _port(port), _listenSock(std::make_unique<TcpSocket>()), _pfd(nullptr), _size(0), _capacity(0)
    {
        // 创建tcp服务端
        _listenSock->BuildListenSocket(InetAddr(port, "0"));
    }

    void AcceptClient()
    {
        InetAddr clientAddr;
        int newfd = _listenSock->Accepter(clientAddr); // 这里一定不会被阻塞
        LOG(INFO, "get new link, fd : %d, addr : [%s:%d]\n", newfd, clientAddr.IP().c_str(), clientAddr.Port());

        if (_size == _capacity)
        {
            reserve(2 * _capacity);
        }

        _pfd[_size].fd = newfd;
        _pfd[_size].events = POLLIN;
        _size++;
        LOG(INFO, "%d add to select array!\n", newfd);

        LOG(INFO, "curr fdArr : %s\n", GetStringFd().c_str());
    }
    void ServiceIO(int pos)
    {
        int fd = _pfd[pos].fd;
        char buff[1024];
        ssize_t n = ::read(fd, buff, sizeof(buff) - 1);
        if (n > 0)
        {
            buff[n] = 0;
            std::cout << "client say#" << buff << std::endl;
            std::string message = buff;
            ::send(fd, message.c_str(), message.size(), 0);
        }
        else if (n == 0)
        {
            LOG(INFO, "%d is closed\n", fd);
            ::close(fd);
            remove(pos);
            LOG(INFO, "curr fdArr : %s\n", GetStringFd().c_str());
        }
        else
        {
            LOG(ERROR, "recv error\n");
            ::close(fd);
            remove(pos);
            LOG(INFO, "curr fdArr : %s\n", GetStringFd().c_str());
        }
    }
    void HandlerEvent()
    {
        for (int i = 0; i < _size; i++)
        {
            if (_pfd[i].revents & POLLIN)
            {
                // listen sock
                if (_pfd[i].fd == _listenSock->Sockfd())
                {
                    AcceptClient();
                }
                // normal sock
                else
                {
                    ServiceIO(i);
                }
            }
        }
    }
    void Loop()
    {
        reserve(4);
        _pfd[0].fd = _listenSock->Sockfd();
        _pfd[0].events = POLLIN;
        _size++;
        while (true)
        {
            int timeout = -1;
            int n = poll(_pfd, _size, timeout);
            switch (n)
            {
            case 0:
                LOG(INFO, "time out.... \n");
                break;
            case -1:
                LOG(ERROR, "select error\n");
                break;
            default:
                HandlerEvent();
                break;
            }
        }
    }

    std::string GetStringFd()
    {
        std::string ret;
        for (int i = 0; i < _size; i++)
        {
            ret += std::to_string(_pfd[i].fd) + "   ";
        }
        return ret;
    }

private:
    uint16_t _port;
    std::unique_ptr<Socket> _listenSock;

    struct pollfd *_pfd;
    int _size;
    int _capacity;
};
